import json
import re

from django.shortcuts import render
from django.views import View
from apps.users.models import User
from django.http import JsonResponse

# Create your views here.

"""
判断用户名是否重复:
1.前端发送请求    
2.后端接收后查询数据库   如果存在则返回1，否则返回0
3.响应json {code(状态码), count(查询结果), errmsg(错误信息)}
路由: usernames/<username>/count/
"""


# 判断网页
class UsernameCountView(View):
    # 定义get函数
    def get(self, request, username):
        # 得到username时判断是否符合要求
        # 查询数据库
        count = User.objects.filter(username=username).count()
        # 返回json数据
        response = JsonResponse({'code': 0, 'count': count, 'errmsg': 'ok'})
        return response


"""
判断手机号是否重复:
1.前端发送请求    
2.后端接收后查询数据库   如果存在则返回1，否则返回0
3.响应json {code(状态码), count(查询结果), errmsg(错误信息)}
路由: mobiles/<mobile>/count/
"""


# 判断网页
class MobileCountView(View):
    # 定义get函数
    def get(self, request, mobile):
        # 查询数据库
        count = User.objects.filter(mobile=mobile).count()
        # 返回json数据
        response = JsonResponse({'code': 0, 'count': count, 'errmsg': 'ok'})
        return response


"""
注册功能的实现
我们应该对前端的所有数据都进行检查(尽管前端会对数据进行检查)
前端:
用户会输入用户名，密码，确认密码，手机号，是否同意协议
点击注册 之后前端会发送axios请求

后端:
接收请求(JSON) 获取数据
确认数据，写入数据库
相应 JSON {'code':0, 'errmsg':'ok'}
路由 POST register/

步骤:
# 1.接收请求(POST...JSON)
# 2.获取数据
# 3.验证数据
#     3.1 用户名，密码，确认密码，手机号，是否同意协议
#     3.2 用户名满足规则，不能重复
#     3.3 密码满足规则
#     3.4 确认密码要与密码一致
#     3.5 手机号满足规则且不能重复
#     3.6 需要同意协议
# 4.数据入库
# 5.返回响应
"""


# 用户注册
class RegisterView(View):
    def post(self, request):
        # 1.接收请求(POST...JSON)
        body = request.body
        body_str = body.decode('utf-8')
        body_dict = json.loads(body_str)
        # 2.获取数据
        username = body_dict.get('username')
        password = body_dict.get('password')
        password2 = body_dict.get('password2')
        mobile = body_dict.get('mobile')
        sms_code = body_dict.get('sms_code')
        allow = body_dict.get('allow')
        # 3.验证数据
        #     3.1 用户名，密码，确认密码，手机号，是否同意协议
        # all([...]) 该函数会判断方括号里面的数据,如果里面有None或者False就返回False
        if not all([username, password, password2, mobile, allow]):
            return JsonResponse({'code': 400, 'errmsg': '参数不全'})

        #     3.2 用户名满足规则，不能重复
        if not re.match('[a-zA-Z0-9_-]{5,20}', username):
            return JsonResponse({'code': 400, 'errmsg': '用户名不满足规则'})
        if User.objects.filter(username=username).count() != 0:
            return JsonResponse({'code': 400, 'errmsg': '该用户名太火爆请换一个吧~'})

        #     3.3 密码满足规则
        if len(password) < 8 or len(password) > 20:
            return JsonResponse({'code': 400, 'errmsg': '密码不符合规则'})

        #     3.4 确认密码要与密码一致
        if password != password2:
            return JsonResponse({'code': 400, 'errmsg': '两次密码不一致'})

        #     3.5 手机号满足规则且不能重复
        if not re.match('^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400, 'errmsg': '手机号不符合规则'})
        if User.objects.filter(mobile=mobile).count() != 0:
            return JsonResponse({'code': 400, 'errmsg': '该手机号已注册！'})

        #     3.6 验证短信验证码
        from django_redis import get_redis_connection
        redis_cli = get_redis_connection('verify_code')
        # 从redis中拿到短信验证码
        redis_sms_code = redis_cli.get(mobile).decode()
        if redis_sms_code is None:
            return JsonResponse({'code': 400, 'errmsg': '短信验证码已过期！'})
        if redis_sms_code != sms_code:
            return JsonResponse({'code': 400, 'errmsg': '短信验证码错误，请重试！'})

        #     3.6 需要同意协议
        if not allow:
            return JsonResponse({'code': 400, 'errmsg': '请同意协议！'})

        # 4.数据入库
        # 这种方法可以写入数据库但是数据库里面的密码没有加密
        # User.objects.create(
        #     username=username,
        #     password=password,
        #     mobile=mobile,
        # )
        # 下面方法写入的密码会加密
        user = User.objects.create_user(
            username=username,
            password=password,
            mobile=mobile,
        )

        """
        在注册成功后可以选择登录或者不登录
        如果选择登录:此时实现状态保持(注册成功即登录)
        如果选择不登陆:不用实现状态保持(注册成功不登录)
        实现状态保持主要两种方式
            在客户端存储信息用Cookie
            在服务端存储信息用Session
        """
        # 系统提供了状态保持的方法
        from django.contrib.auth import login
        # request, user
        # 状态保持 -- 登录用户的状态保持
        # user 已经登录的用户信息
        login(request, user)
        # 5.返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


"""
用户登录

前端
    用户输入账号密码后点击登录，前端会发送axios请求
    
后端
    请求: 接收数据，验证数据
    业务逻辑: 验证用户名和密码是否正确，session
    响应: 返回json数据，0成功，400失败
    
    POSt    /login/
步骤:
    1. 接收数据
    2. 验证数据
    3. 验证用户名密码是否正确
    4. session
    5. 判断是否记住登录
    6. 返回响应
"""


# 用户登录
class LoginView(View):
    def post(self, request):
        # 1. 接收数据 (非表单数据)
        data = json.loads(request.body.decode('utf-8'))
        username = data.get('username')
        password = data.get('password')
        remembered = data.get('remembered') # 这里前端返回的是bool类型

        # 2. 验证数据
        if not all([username, password]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必要参数！'})

        # 判断用户是用手机号还是用户名登录
        if re.match('1[3-9]\d{9}', username):
            User.USERNAME_FIELD = 'mobile'
        else:
            User.USERNAME_FIELD = 'username'

        # 3. 验证用户名密码是否正确
        # 方式一 利用模型类操作数据库
        # user = User.objects.get(username=username)
        # 方式二 利用django自带的方法验证用户名密码
        from django.contrib.auth import authenticate
        # authenticate  如果正确返回user信息,否则返回None
        user = authenticate(username=username, password=password)
        if user is None:
            return JsonResponse({'code': 400, 'errmsg': '账号或密码错误！'})

        # 4. session
        from django.contrib.auth import login
        login(request, user)

        # 5. 判断是否记住登录
        if remembered:
            # 根据实际要求设置保存时间 参数:保存时间
            request.session.set_expiry(None)
        else:
            request.session.set_expiry(0)

        # 6. 返回响应
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        # 在首页设置显示用户名。通过设置cookie实现 (也可以设置axios请求，但是由于发送的是网络请求对服务器要求较高)
        response.set_cookie('username', username)   # 这里cookie根据实际情况设置保存时间
        return response


# 退出功能
"""
前端
    用户点击退出按钮的时候，前端发送一个asios delete请求
后端
    请求
    业务逻辑 退出
    响应  返回JSON数据
"""
from django.contrib.auth import logout


class LogoutView(View):
    def get(self, request):
        # 1. 删除session信息
        logout(request)
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        # 2. 删除cookie信息 因为前端根据cookie信   息判断是否登录   考虑具体需求
        response.delete_cookie('username')
        return response


# 用户中心,也必须是登录用户
"""
from django.contrib.auth.mixins import LoginRequiredMixin
class CenterView(LoginRequiredMixin, View):
    def get(self, request):
        return JsonResponse({'code': 0, 'errmsg': 'ok'})
这种方法如果未登录会重定向，并不会返回JSON数据 我们需要返回JSON数据
所以我们需要更改LoginRequiredMixin方法中返回类型
"""
from utils.views import LoginRequiredJSONMixin


class CenterView(LoginRequiredJSONMixin, View):
    def get(self, request):
        # request.user 就是已经登录的用户信息 来源于中间件
        # 系统会进行判断，如果确实是登录用户，则可以获取到登录用户对应的模型实例数据
        # 如果不是登录用户，则 request.user = AnonymousUser() 匿名用户
        info_data = {
            'username': request.user.username,
            'email': request.user.email,
            'mobile': request.user.mobile,
            'email_active': request.user.email_active,
        }
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'info_data': info_data})


"""
需求:     1. 保存邮箱地址    2. 发送一封激活文件    3. 用户激活邮件

前端:
    当用户输入邮箱之后，点击保存，这个时候会发送axios请求
后端:
    请求  接收请求获取数据
    业务逻辑  保存邮箱地址并发送一封激活邮件 
    响应  JSON code=0
    
    路由  PUT
    步骤  
        1. 接收请求
        2. 获取数据
        3. 保存邮箱地址
        4. 发送一封激活邮件
        5. 返回响应
"""


class EmailView(LoginRequiredJSONMixin, View):
    def put(self, request):
        # 1. 接收请求
        data = json.loads(request.body.decode('utf-8'))

        # 2. 获取数据
        email = data.get('email')
        # 应该加上正则判断email (后面写)

        # 3. 保存邮箱地址
        user = request.user
        user.email = email
        user.save()

        # 4. 发送一封激活邮件(后面写)
        from django.core.mail import send_mail
        # send_mail 具有四个参数
        # subject, 主题(标题)
        subject = '美多商城激活邮件'
        # message, 邮件内容
        message = ""
        # from_email, 发件人 <> 中填写发件人邮箱地址
        from_email = '美多商城<EMAIL>'
        # recipient_list, 收件人列表
        recipient_list = [] # 将收件人放入列表中
        # 邮件的内容如果是和html相关比如一些链接 我们就得用 html_message 参数
        # 我们想要点击链接激活后能判断是否是本人(或者邮箱对应)所以我们需要在a标签的链接中加入验证数据需要加密
        from apps.users.utils import generic_email_verify_token
        token = generic_email_verify_token(request.user.id)

        verify_url = "http://www.meiduo.site:8080/success_verify_email.html?token=%s"%token
        # 组织我们的激活邮件
        html_message = '<p>尊敬的用户您好！</p>' \
                       '<p>感谢您使用美多商城。</p>' \
                       '<p>您的邮箱为：%s 。请点击此链接激活您的邮箱：</p>' \
                       '<p><a href="%s">%s<a></p>' % (email, verify_url, verify_url)

        # send_mail(
        #     subject=subject,
        #     message=message,
        #     from_email=from_email,
        #     recipient_list=recipient_list,
        #     html_message=html_message,
        # )

        from celery_tasks.email.tasks import celery_send_emails
        celery_send_emails.delay(
            subject=subject,
            message=message,
            from_email=from_email,
            recipient_list=recipient_list,
            html_message=html_message,
        )

        # 5. 返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


"""
需求: 激活用户的邮件
后端: 
    请求: 接收请求，获取参数，验证参数
    业务逻辑: user_id, 根据用户id查询数据，修改数据
    响应: 返回响应JSON
    
    路由:     PUT emails/verification/ (说明:token并没有在body里面)
    步骤: 
        1. 接收请求
        2. 获取参数
        3. 验证参数
        4. 获取user_id
        5. 根据用户id查询数据
        6. 修改数据
        7. 返回响应JSON
"""


class EmailVerifyView(View):

    def put(self, request):
        # 1. 接收请求
        params = request.GET
        # 2. 获取参数
        token = params.get('token')
        # 3. 验证参数
        if token is None:
            return JsonResponse({'code': 400, 'errmsg': '参数缺失!'})
        # 4. 获取user_id
        from apps.users.utils import check_verify_token
        user_id = check_verify_token(token)
        if user_id is None:
            return JsonResponse({'code': 400, 'errmsg': '参数错误!'})
        # 5. 根据用户id查询数据
        user = User.objects.get(id=user_id)
        # 6. 修改数据
        user.email_active = True
        user.save()
        # 7. 返回响应JSON
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


"""
新增收件地址
请求
业务逻辑 (数据库的增删改查)
响应

增 
    1.接收数据
    2.验证数据
    3.数据入库
    4.返回响应
删
    1.查询到指定数据
    2.删除数据(物理删除 逻辑删除)
    3.返回响应
改 
    1.查询到数据
    2.接收数据
    3.验证数据
    4.数据入库
    5.返回响应
查
    1.查询指定数据
    2.将对象数据转换为字典数据
    3.放回响应
"""

"""
需求: 新增地址
前端: 当用户填写完成地址信息，前端应该发送一个axios请求，会携带相关信息，(POST--body)
后端: 
    请求: 接收请求，获取参数，验证参数
    业务逻辑: 数据入库
    响应: 返回响应
    
    路由: POST    /addresses/create/
    步骤:
        1.接收请求
        2.获取参数
        3.数据入库
        4.返回响应
"""

from apps.users.models import Address


# 以登录用户去访问就 LoginRequiredJSONMixin
class AddressCreateView(LoginRequiredJSONMixin, View):
    def post(self, request):
        # 1.接收请求
        data = json.loads(request.body.decode('utf-8'))
        # 2.获取参数
        receiver = data.get('receiver')
        province_id = data.get('province_id')
        city_id = data.get('city_id')
        district_id = data.get('district_id')
        place = data.get('place')
        mobile = data.get('mobile')
        tel = data.get('tel')
        email = data.get('email')
        user = request.user
        # 验证参数  (省略)
        # 2.1 验证必传参数
        # 3.数据入库
        address = Address.objects.create(
            user=user,
            title=receiver,
            receiver=receiver,
            province_id=province_id,
            city_id=city_id,
            district_id=district_id,
            place=place,
            mobile=mobile,
            tel=tel,
            email=email,
        )

        address_dict = {
            'id': address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        # 4.返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'address_dict': address_dict})


# 查询(新增数据后要在网页上显示)
class AddressView(LoginRequiredJSONMixin, View):
    def get(self, request):
        # 1.查询指定数据
        addresses = Address.objects.filter(user=request.user, is_deleted=False)
        # 2.将对象数据转换为字典数据
        address_list = []
        for address in addresses:
            address_list.append({
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            })
        # 3.返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'addresses': address_list})


# 地址的修改 && 地址的删除
class ChangeAddressView(LoginRequiredJSONMixin, View):
    # 地址的修改
    def put(self, request, id):
        # 1.接收请求
        data = json.loads(request.body.decode('utf-8'))

        # 2.获取参数
        receiver = data.get('receiver')
        province_id = data.get('province_id')
        city_id = data.get('city_id')
        district_id = data.get('district_id')
        place = data.get('place')
        mobile = data.get('mobile')
        tel = data.get('tel')
        email = data.get('email')
        user = request.user

        # 3.参数入库(数据更新)
        Address.objects.filter(id=id, is_deleted=False).update(
            user=request.user,
            title=receiver,
            receiver=receiver,
            province_id=province_id,
            city_id=city_id,
            district_id=district_id,
            place=place,
            mobile=mobile,
            tel=tel,
            email=email,
        )

        address = Address.objects.get(id=id, is_deleted=False)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        # 返回响应
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'address': address_dict})

    # 地址的删除
    def delete(self, request, id):
        Address.objects.filter(id=id, is_deleted=False).update(is_deleted=True)
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


# 设置默认地址
class DefaultAddressView(LoginRequiredJSONMixin, View):
    def put(self, request, id):
        # 通过id查询到对应的地址
        address = Address.objects.get(id=id, is_deleted=False)
        # 将User模型里面的default_address更改为address里面的id
        # 外键设置好后后自动加上 _id 的后缀
        # 这里注意的是由于default_address是设置的外键，所以下面可以直接赋值，Django会自动检测到对应的id存到数据库
        User.objects.filter(id=id).update(default_address=address)

        return JsonResponse({'code': 0, 'errmsg': 'ok'})


# 修改密码
class ChangePasswordView(LoginRequiredJSONMixin, View):
    def put(self, request):
        data = json.loads(request.body.decode('utf-8'))
        old_password = data.get('old_password')
        new_password = data.get('new_password')
        new_password2 = data.get('new_password2')

        # 验证参数
        if not all([old_password, new_password, new_password2]):
            return JsonResponse({'code': 400, 'errmsg': '参数错误!'})

        # 判断原始密码
        if not request.user.check_password(old_password):
            return JsonResponse({'code': 400, 'errmsg': '原始密码错误!'})

        # 判断是否合规
        if len(new_password) < 8 or len(new_password) > 20:
            return JsonResponse({'code': 400, 'errmsg': '密码不符合规则!'})

        if new_password != new_password2:
            return JsonResponse({'code': 400, 'errmsg': '两次密码不一致!'})

        # 修改密码
        request.user.set_password(new_password)
        request.user.save()

        # 清空浏览器状态
        logout(request)
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        response.delete_cookie('username')

        # 返回响应
        return response


































